﻿using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Windows;
using System.Windows.Threading;
using DiPlaner.Remote;
using Zeta.Common;
using Zeta.CommonBot;
using Zeta.Common.Plugins;

namespace DiPlaner.Remote
{
    [ServiceContract]
    interface IRemotingApi
    {
        [OperationContract]
        bool Init(int dbPID);

        [OperationContract(IsOneWay = true)]
        void RestartDB(int dbPID);

        [OperationContract(IsOneWay = true)]
        void RestartD3(int dbPID);

        [OperationContract]
        string[] GetProfileNames();

        [OperationContract]
        string GetCurrentProfileName(int dbPID);

        [OperationContract(IsOneWay = true)]
        void StartProfile(string profileName);

        [OperationContract(IsOneWay = true)]
        void StopProfile(string profileName);

        [OperationContract(IsOneWay = true)]
        void PauseProfile(string profileName);

        [OperationContract(IsOneWay = true)]
        void IdleProfile(string profileName, TimeSpan time);

        [OperationContract(IsOneWay = true)]
        void Logon(string key, int pid, string bnet_acc, string bnet_pass, string title, string routine, int minutes, bool noflash, bool autostart);

        [OperationContract]
        int GetProfileStatus(string profileName);

        [OperationContract(IsOneWay = true)]
        void SetProfileStatusText(int dbPID, string status);

        [OperationContract(IsOneWay = true)]
        void SetBotInfoToolTip(int dbPID, string tooltip);

        [OperationContract(IsOneWay = true)]
        void SkipCurrentTask(string profileName);
    }
}


namespace DiPlaner.DBPlugin
{
    public class DiPlugin : IPlugin
    {
        static public bool IsConnected { get; private set; }
        static internal IRemotingApi RemoteAPI { get; private set; }
        static internal int dbPID { get; private set; }
        static internal string CurProfileName { get; private set; }
        static DispatcherTimer _monitorTimer;
        static ChannelFactory<IRemotingApi> _pipeFactory;
        static internal DiPlugin Instance { get; private set; }

        public DiPlugin() { OnInitialize(); }
        
        #region Implementation of IPlugin

        public void OnPulse()
        {
        }

        public void OnInitialize()
        {
            Instance = this;
            try 
            {
                AppDomain.CurrentDomain.ProcessExit += CurrentDomainProcessExit;
                AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;

                dbPID = Process.GetCurrentProcess().Id;
                _pipeFactory = new ChannelFactory<IRemotingApi>(new NetNamedPipeBinding(),
                    new EndpointAddress("net.pipe://localhost/DiPlaner/Server"));

                RemoteAPI = _pipeFactory.CreateChannel();

                Application.Current.Dispatcher.Invoke(new Action(
                    delegate
                        {
                            _monitorTimer = new DispatcherTimer();
                            _monitorTimer.Tick += MonitorTimerCb;
                            _monitorTimer.Interval = TimeSpan.FromSeconds(10);
                            _monitorTimer.Start();
                        }));
                IsConnected = RemoteAPI.Init(dbPID);
                if (IsConnected)
                    CurProfileName = RemoteAPI.GetCurrentProfileName(dbPID);

            } catch { }
        }

        public void OnShutdown() 
        {
            try {
                if (_pipeFactory.State == CommunicationState.Opened || _pipeFactory.State == CommunicationState.Opening) {
                    _pipeFactory.Close();
                    _pipeFactory.Abort();
                }
            } catch { }
        }

        public void OnEnabled()
        {
        }
        public void OnDisabled()
        {
        }
        public bool Equals(IPlugin other)
        {
            return false;
        }
        public string Author { get { return "ZloVreD"; } }
        public Version Version { get { return new Version(1, 0); } }
        public string Name { get { return "DiPlugin"; } }
        public string Description { get { return "DiPlaner helper"; } }
        public Window DisplayWindow
        {
            get
            {
                Logging.Write("IsConnected {0}", IsConnected);
                foreach (var name in RemoteAPI.GetProfileNames())
                {
                    Logging.Write("{0}: GetProfileStatus: {1}", name, RemoteAPI.GetProfileStatus(name));
                    RemoteAPI.SetProfileStatusText(dbPID, BotMain.StatusText);
                }
                return null;
            }
        }
        
        #endregion

        static string _lastStatus;
        static string _lastTooltip;
        static DateTime _runningTimeStamp = DateTime.Now;
        public static void MonitorTimerCb(object sender, EventArgs e)
        {
            try
            {
                if (!IsConnected) return;

                if (!BotMain.IsRunning) {
                    var profStatus = RemoteAPI.GetProfileStatus(CurProfileName);
                    if (profStatus != 1 && DateTime.Now - _runningTimeStamp >= TimeSpan.FromSeconds(60))
                        RemoteAPI.RestartDB(dbPID);
                } else
                    _runningTimeStamp = DateTime.Now;

                if (BotMain.StatusText != _lastStatus && !string.IsNullOrEmpty(BotMain.StatusText))
                {
                    RemoteAPI.SetProfileStatusText(dbPID, BotMain.StatusText);
                    _lastStatus = BotMain.StatusText;
                }

                if (GameStats.Instance.GamesPerHour > 0)
                    UpdateTooltipInfo();

            } catch (Exception ex) {
                Logging.WriteException(ex);
            }
        }
        private static void UpdateTooltipInfo()
        {
            try
            {
                var tooltip = string.Empty;

                if (Zeta.ZetaDia.Me.Level < Zeta.ZetaDia.Me.LevelCap)
                    tooltip += string.Format("XP/Hr: {0} ", GameStats.Instance.ExpPerHour);
                tooltip += string.Format("Gold/Hr: {0}\n", GameStats.Instance.GoldPerHour);

                tooltip += string.Format("Games: {0} ({1}/Hr)\n",
                    GameStats.Instance.NumGamesJoined, GameStats.Instance.GamesPerHour);
                tooltip += string.Format("Death/Hr: {0}", GameStats.Instance.DeathsPerHour);

                if (tooltip == _lastTooltip) return;
                RemoteAPI.SetBotInfoToolTip(dbPID, tooltip);
                _lastTooltip = tooltip;

            } catch (Exception ex) {
                Logging.WriteException(ex);
            }
        }

        void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            OnShutdown();
        }
        void CurrentDomainProcessExit(object sender, EventArgs e) 
        {
            OnShutdown();
        }
    }

    static public class DiPluginApi
    {
        static int dbPID { get { return DiPlugin.dbPID; } }
        static IRemotingApi RemoteAPI { get { return DiPlugin.RemoteAPI; } }
        public static bool IsConnected { get { return DiPlugin.IsConnected; } }
        public static string CurrentProfileName { get { return DiPlugin.CurProfileName; } }
        public static void RestartD3() { RemoteAPI.RestartD3(dbPID); }
        public static void RestartDB() { RemoteAPI.RestartDB(dbPID); }
        public static string[] GetProfileNames() { return RemoteAPI.GetProfileNames(); }
        public static void StartProfile(string profileName) { RemoteAPI.StartProfile(profileName); }
        public static void StopProfile(string profileName) { RemoteAPI.StopProfile(profileName); }
        public static void PauseProfile(string profileName) { RemoteAPI.PauseProfile(profileName); }
        public static void IdleProfile(string profileName, TimeSpan time) {
            RemoteAPI.IdleProfile(profileName, time);
        }

        public static void Logon(string key, int pid, string bnet_acc, string bnet_pass, string title, string routine, int minutes, bool noflash, bool autostart) {
            RemoteAPI.Logon(key, pid, bnet_acc, bnet_pass, title, routine, minutes, noflash, autostart);
        }

        public static int GetProfileStatus(string profileName) {
            return RemoteAPI.GetProfileStatus(profileName);
        }

        public static void SetProfileStatusText(string status) {
            RemoteAPI.SetProfileStatusText(dbPID, status);
        }

        public static void SkipCurrentTask(string profileName) {
            RemoteAPI.SkipCurrentTask(profileName);
        }
    }
}
